package com.roger.ndtplayer.base

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.gson.JsonIOException
import com.roger.ndtplayer.R
import com.roger.ndtplayer.commom.api.ApiException
import com.roger.ndtplayer.commom.api.RetrofitClient
import com.roger.ndtplayer.util.LogUtils
import com.roger.ndtplayer.util.isNetAvailable
import com.roger.ndtplayer.util.showToast
import kotlinx.coroutines.*
import retrofit2.HttpException
import java.net.ConnectException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import javax.net.ssl.SSLHandshakeException

typealias Block<T> = suspend (CoroutineScope) -> T
typealias Error = suspend (Exception) -> Unit
typealias Cancel = suspend (Exception) -> Unit

/**
 * BaseViewModel
 */
open class BaseViewModel : ViewModel() {

    val loginStatusInvalid = MutableBooleanLiveData()
    val showDialog = MutableBooleanLiveData()
    val dialogMsg = MutableStringLiveData()
    val showErrorView = MutableBooleanLiveData()
    val showEmptyView = MutableBooleanLiveData()
    val showNoNetView = MutableBooleanLiveData()
    val showSuccess = MutableBooleanLiveData()
    val opetateSuccess = MutableBooleanLiveData()


    init {
        dialogMsg.value = "正在加载中"
        showSuccess.value = true
    }


    protected fun launchOnMain(
        block: Block<Unit>,
        error: Error? = null,
        cancel: Cancel? = null,
        showErrorToast: Boolean = true,
        isShowDialog: Boolean = true
    ): Job {
        showDialog.postValue(isShowDialog)
        return viewModelScope.launch {
            withContext(Dispatchers.Main,
                block = {
                    if (isNetAvailable()) {
                        try {
                            block.invoke(this)
                        } catch (e: Exception) {
                            showDialog.value = !isShowDialog
                            when (e) {
                                is CancellationException -> {
                                    cancel?.invoke(e)
                                }
                                else -> {
                                    onError(e, showErrorToast)
                                    error?.invoke(e)
                                }
                            }
                        } finally {
                            showDialog.value = !isShowDialog
                        }
                    } else {
                        showDialog.value = !isShowDialog
                        onError(java.lang.Exception("网络不可用,请检查手机网络"), true)
                    }
                })
        }
    }

    /**
     * 此方法只能执行异步任务
     * 不能更新UI，postValue
     */
    protected fun launchOnIO(
        block: Block<Unit>,
        error: Error? = null,
        cancel: Cancel? = null,
        showErrorToast: Boolean = true,
        isShowDialog: Boolean = true
    ): Job {
        showDialog.postValue(isShowDialog)
        return viewModelScope.launch {
            withContext(Dispatchers.IO,
                block = {
                    if (isNetAvailable()) {
                        try {
                            block.invoke(this)
                        } catch (e: Exception) {
                            showDialog.postValue(!isShowDialog)
                            when (e) {
                                is CancellationException -> {
                                    cancel?.invoke(e)
                                }
                                else -> {
                                    onError(e, showErrorToast)
                                    error?.invoke(e)
                                }
                            }
                        } finally {
                            showDialog.postValue(!isShowDialog)
                        }
                    } else {
                        showDialog.postValue(!isShowDialog)
                        onError(java.lang.Exception("网络不可用,请检查手机网络"), true)
                    }
                })
        }
    }

    /**
     * 创建并执行协程
     * @param block 协程中执行
     * @return Deferred<T>
     */
    protected fun <T> async(block: Block<T>): Deferred<T> {
        return viewModelScope.async {
            block.invoke(this)
        }
    }

    /**
     * 取消协程
     * @param job 协程job
     */
    protected fun cancelJob(job: Job?) {
        if (job != null && job.isActive && !job.isCompleted && !job.isCancelled) {
            job.cancel()
        }
    }

    /**
     * 统一处理错误
     * @param e 异常
     * @param showErrorToast 是否显示错误吐司
     */
    private fun onError(e: Exception, showErrorToast: Boolean) {
        if (androidx.databinding.library.BuildConfig.DEBUG) {
            LogUtils.e("error", e.localizedMessage)
        }

        when (e) {
            is ApiException -> {
                when (e.code) {
                    "461", "A4600", "460" -> {
                        // 登录失效，清除用户信息、清除cookie/token
//                        UserInfoStore.clearUserInfo()
                        RetrofitClient.clearCookie()
                        loginStatusInvalid.postValue(true)
                    }
                    // 其他api错误
                    "-1" -> if (showErrorToast) {
                        LogUtils.e("error", e.localizedMessage)
                        showToast(e.message)
                    }
                    // 其他错误
                    else -> if (showErrorToast) {
                        LogUtils.e("error", e.localizedMessage)
                        showToast(e.message)
                    }
                }
            }
            // 网络请求失败
            is ConnectException,
            is SocketTimeoutException,
            is UnknownHostException,
            is HttpException,
            is SSLHandshakeException -> {
                showNoNetView.postValue(true)
                if (showErrorToast) showToast(R.string.network_request_failed)
            }
            // 数据解析错误
            is JsonIOException, is JsonIOException -> {
                showErrorView.postValue(true)
                if (showErrorToast) showToast(R.string.api_data_parse_error)
            }
            // 其他错误
            else ->
                if (showErrorToast) showToast(e.message ?: return)
        }
    }


    class MutableIntLiveData(value: Int = 0) : MutableLiveData<Int>(value) {
        override fun getValue(): Int {
            return super.getValue() ?: 0
        }
    }

    class MutableDoubleLiveData(value: Double = 0.0) : MutableLiveData<Double>(value) {
        override fun getValue(): Double {
            return super.getValue() ?: 0.0
        }
    }

    class MutableStringLiveData(value: String = "") : MutableLiveData<String>(value) {
        override fun getValue(): String {
            return super.getValue() ?: ""
        }
    }

    class MutableBooleanLiveData(value: Boolean = false) : MutableLiveData<Boolean>(value) {
        override fun getValue(): Boolean {
            return super.getValue() ?: false
        }

        fun toggle() {
            postValue(!value)
        }
    }

    open class StringLiveData(value: String = "") : LiveData<String>(value) {
        override fun getValue(): String {
            return super.getValue() ?: ""
        }
    }
}